
error_counts = 0

while True:
    try:
        import sys
        import tkinter as tk
        from tkinter import ttk, scrolledtext, messagebox, filedialog, simpledialog
        import socket
        import threading
        import json
        import sqlite3
        import os
        import base64
        import hashlib
        import re
        import configparser
        from datetime import datetime
        import markdown
        from bs4 import BeautifulSoup
        import html as html_parser
        from PIL import Image, ImageTk, ImageDraw
        import io
        import uuid
        import logging
        import requests
        from collections import defaultdict
        import cv2
        import pygame
        import numpy as np
        import screeninfo
        from pygame import mixer
        import time
        import sys
        import platform
        import subprocess

        break
    except ModuleNotFoundError:
        if error_counts >= 3:
            print("下载所需库失败TAT 尝试升级 pip 或者检查网络!")
            exit(100)
        os.system("pip install markdown beautifulsoup4 pillow requests opencv-python pygame numpy screeninfo platform subprocess")
        error_counts += 1

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 配置常量
SERVER_IP = "26.88.69.206"
SERVER_PORT = 2333
BUFFER_SIZE = 65536  # 增加缓冲区大小以处理大文件
MAX_FILE_SIZE = 10240000  # 10MB
IMAGE_CACHE_DIR = ".chat/image"
MESSAGE_STORAGE_DIR = ".chat/messages"

# 确保目录存在
os.makedirs(IMAGE_CACHE_DIR, exist_ok=True)
os.makedirs(MESSAGE_STORAGE_DIR, exist_ok=True)

# 版本信息
CLIENT_VERSION = "1.0.0"


class ChatServer:
    def __init__(self, host='0.0.0.0', port=SERVER_PORT):  # 修改为 '0.0.0.0' 以监听所有接口
        self.host = host
        self.port = port
        self.clients = {}
        self.groups = {}
        self.user_ips = defaultdict(int)
        self.running = True

        # 添加详细的启动日志
        logging.info(f"尝试启动服务器在 {self.host}:{self.port}")

        self.setup_database()

        try:
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((self.host, self.port))
            self.server_socket.listen(50)
            logging.info(f"服务器成功启动在 {self.host}:{self.port}")
        except Exception as e:
            logging.error(f"服务器启动失败: {e}")
            # 尝试使用备用端口
            try:
                self.port = 2334  # 尝试备用端口
                self.server_socket.bind((self.host, self.port))
                self.server_socket.listen(50)
                logging.info(f"服务器成功启动在备用端口 {self.host}:{self.port}")
            except Exception as e2:
                logging.error(f"服务器启动完全失败: {e2}")
                raise

    def signal_handler(self, signum, frame):
        """处理中断信号"""
        logging.info("接收到终止信号，正在关闭服务器...")
        self.running = False
        self.server_socket.close()
        if hasattr(self, 'conn'):
            self.conn.close()
        sys.exit(0)

    def setup_database(self):
        self.conn = sqlite3.connect('chat_server.db', check_same_thread=False)
        self.cursor = self.conn.cursor()

        # 创建用户表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT UNIQUE NOT NULL,
                password TEXT NOT NULL,
                uuid TEXT UNIQUE NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')

        # 检查并添加last_ip列（如果不存在）
        try:
            self.cursor.execute("SELECT last_ip FROM users LIMIT 1")
        except sqlite3.OperationalError:
            try:
                self.cursor.execute("ALTER TABLE users ADD COLUMN last_ip TEXT")
                logging.info("已添加last_ip列到users表")
            except sqlite3.OperationalError as e:
                logging.error(f"添加last_ip列失败: {e}")

        # 检查并添加avatar列（如果不存在）
        try:
            self.cursor.execute("SELECT avatar FROM users LIMIT 1")
        except sqlite3.OperationalError:
            try:
                self.cursor.execute("ALTER TABLE users ADD COLUMN avatar BLOB")
                logging.info("已添加avatar列到users表")
            except sqlite3.OperationalError as e:
                logging.error(f"添加avatar列失败: {e}")

        # 创建好友关系表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS friends (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_uuid TEXT NOT NULL,
                friend_uuid TEXT NOT NULL,
                status TEXT DEFAULT 'accepted',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_uuid) REFERENCES users (uuid),
                FOREIGN KEY (friend_uuid) REFERENCES users (uuid)
            )
        ''')

        # 创建黑名单表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS blacklist (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_uuid TEXT NOT NULL,
                blocked_uuid TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_uuid) REFERENCES users (uuid),
                FOREIGN KEY (blocked_uuid) REFERENCES users (uuid)
            )
        ''')

        # 创建群组表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS groups (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                uuid TEXT UNIQUE NOT NULL,
                name TEXT NOT NULL,
                owner_uuid TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (owner_uuid) REFERENCES users (uuid)
            )
        ''')

        # 创建群组成员表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS group_members (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                group_uuid TEXT NOT NULL,
                member_uuid TEXT NOT NULL,
                role TEXT DEFAULT 'member',
                joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (group_uuid) REFERENCES groups (uuid),
                FOREIGN KEY (member_uuid) REFERENCES users (uuid)
            )
        ''')

        # 检查并添加title、custom_nickname和is_muted列（如果不存在）
        columns_to_add = [
            ('title', 'TEXT DEFAULT ""'),
            ('custom_nickname', 'TEXT DEFAULT ""'),
            ('is_muted', 'INTEGER DEFAULT 0')
        ]

        for column_name, column_type in columns_to_add:
            try:
                self.cursor.execute(f"SELECT {column_name} FROM group_members LIMIT 1")
            except sqlite3.OperationalError:
                try:
                    self.cursor.execute(f"ALTER TABLE group_members ADD COLUMN {column_name} {column_type}")
                    logging.info(f"已添加{column_name}列到group_members表")
                except sqlite3.OperationalError as e:
                    logging.error(f"添加{column_name}列失败: {e}")

        # 创建消息表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS messages (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                sender_uuid TEXT NOT NULL,
                receiver_uuid TEXT NOT NULL,
                message_type TEXT DEFAULT 'text',
                content TEXT NOT NULL,
                mode TEXT DEFAULT 'normal',
                timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (sender_uuid) REFERENCES users (uuid)
            )
        ''')

        # 创建表情包表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS emojis (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                uuid TEXT UNIQUE NOT NULL,
                name TEXT NOT NULL,
                image_data BLOB NOT NULL,
                uploader_uuid TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (uploader_uuid) REFERENCES users (uuid)
            )
        ''')

        # 创建离线消息表
        self.cursor.execute('''
            CREATE TABLE IF NOT EXISTS offline_messages (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_uuid TEXT NOT NULL,
                message_data TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (user_uuid) REFERENCES users (uuid)
            )
        ''')

        self.conn.commit()

    def handle_client(self, client_socket, address):
        logging.info(f"新连接: {address}")
        user_uuid = None
        client_ip = address[0]

        try:
            while self.running:  # 添加运行状态检查
                try:
                    # 设置超时以便定期检查运行状态
                    client_socket.settimeout(1.0)
                    data = client_socket.recv(BUFFER_SIZE).decode('utf-8')
                    if not data:
                        break

                    try:
                        message = json.loads(data)
                        # 版本检测
                        if message.get('type') == 'version_check':
                            client_version = message.get('version', '1.0.0')
                            if client_version != CLIENT_VERSION:
                                response = {
                                    'type': 'version_mismatch',
                                    'server_version': CLIENT_VERSION,
                                    'client_version': client_version
                                }
                                client_socket.send(json.dumps(response).encode('utf-8'))
                                continue
                            else:
                                response = {'type': 'version_ok'}
                                client_socket.send(json.dumps(response).encode('utf-8'))
                                continue

                        response = self.process_message(message, client_socket, client_ip)
                        if response:
                            client_socket.send(json.dumps(response).encode('utf-8'))
                    except json.JSONDecodeError:
                        logging.error(f"无效的JSON数据: {data}")
                except socket.timeout:
                    # 超时是正常的，继续循环检查运行状态
                    continue
                except Exception as e:
                    logging.error(f"接收数据错误: {e}")
                    break

        except Exception as e:
            logging.error(f"处理客户端错误: {e}")
        finally:
            if user_uuid and user_uuid in self.clients:
                del self.clients[user_uuid]
            client_socket.close()
            logging.info(f"连接关闭: {address}")

    def process_message(self, message, client_socket, client_ip):
        msg_type = message.get('type')

        if msg_type == 'register':
            return self.register_user(message, client_ip)
        elif msg_type == 'login':
            response = self.login_user(message, client_ip)
            if response.get('success'):
                user_uuid = response['user']['uuid']
                self.clients[user_uuid] = client_socket
                # 发送离线消息
                self.send_offline_messages(user_uuid, client_socket)
            return response
        elif msg_type == 'send_message':
            return self.send_message(message)
        elif msg_type == 'get_friends':
            return self.get_friends(message)
        elif msg_type == 'add_friend':
            return self.add_friend(message)
        elif msg_type == 'add_friend_response':
            return self.add_friend_response(message)
        elif msg_type == 'block_user':
            return self.block_user(message)
        elif msg_type == 'create_group':
            return self.create_group(message)
        elif msg_type == 'get_groups':
            return self.get_groups(message)
        elif msg_type == 'join_group':
            return self.join_group(message)
        elif msg_type == 'upload_emoji':
            return self.upload_emoji(message)
        elif msg_type == 'get_emojis':
            return self.get_emojis(message)
        elif msg_type == 'get_user_info':
            return self.get_user_info(message)
        elif msg_type == 'update_profile':
            return self.update_profile(message)
        elif msg_type == 'download_chat_history':
            return self.download_chat_history(message)
        elif msg_type == 'group_management':
            return self.group_management(message)
        elif msg_type == 'search_users_groups':
            return self.search_users_groups(message)
        elif msg_type == 'start_call':
            return self.start_call(message)
        elif msg_type == 'get_offline_messages':
            return self.get_offline_messages(message)
        elif msg_type == 'delete_offline_messages':
            return self.delete_offline_messages(message)
        else:
            return {'success': False, 'error': '未知的消息类型', 'type': 'error_response'}

    def register_user(self, message, client_ip):
        username = message.get('username')
        password = message.get('password')

        if not username or not password:
            return {'success': False, 'error': '用户名和密码不能为空', 'type': 'register_response'}

        # 检查IP注册限制
        if self.user_ips[client_ip] >= 2:
            return {'success': False, 'error': '每个IP最多只能注册2个账户', 'type': 'register_response'}

        # 检查用户名是否已存在
        self.cursor.execute("SELECT id FROM users WHERE username = ?", (username,))
        if self.cursor.fetchone():
            return {'success': False, 'error': '用户名已存在', 'type': 'register_response'}

        # 创建用户
        user_uuid = "C" + str(uuid.uuid4())[1:]  # 用户UUID以C开头
        hashed_password = hashlib.sha256(password.encode()).hexdigest()

        self.cursor.execute(
            "INSERT INTO users (username, password, uuid, last_ip) VALUES (?, ?, ?, ?)",
            (username, hashed_password, user_uuid, client_ip)
        )
        self.conn.commit()

        # 更新IP注册计数
        self.user_ips[client_ip] += 1

        return {'success': True, 'message': '注册成功', 'type': 'register_response'}

    def login_user(self, message, client_ip):
        username = message.get('username')
        password = message.get('password')

        if not username or not password:
            return {'success': False, 'error': '用户名和密码不能为空', 'type': 'login_response'}

        hashed_password = hashlib.sha256(password.encode()).hexdigest()

        # 检查avatar列是否存在
        try:
            self.cursor.execute(
                "SELECT uuid, username, avatar FROM users WHERE username = ? AND password = ?",
                (username, hashed_password)
            )
            user = self.cursor.fetchone()
        except sqlite3.OperationalError:
            # 如果avatar列不存在，使用旧的查询
            self.cursor.execute(
                "SELECT uuid, username FROM users WHERE username = ? AND password = ?",
                (username, hashed_password)
            )
            user = self.cursor.fetchone()
            if user:
                user = (user[0], user[1], None)  # 添加空的avatar值

        if user:
            # 更新最后登录IP
            try:
                self.cursor.execute(
                    "UPDATE users SET last_ip = ? WHERE uuid = ?",
                    (client_ip, user[0])
                )
                self.conn.commit()
            except sqlite3.OperationalError as e:
                logging.error(f"更新last_ip失败: {e}")

            avatar_base64 = None
            if user[2]:
                avatar_base64 = base64.b64encode(user[2]).decode('utf-8')

            return {
                'success': True,
                'message': '登录成功',
                'user': {'uuid': user[0], 'username': user[1], 'avatar': avatar_base64},
                'type': 'login_response'
            }
        else:
            return {'success': False, 'error': '用户名或密码错误', 'type': 'login_response'}

    def send_message(self, message):
        sender_uuid = message.get('sender_uuid')
        receiver_uuid = message.get('receiver_uuid')
        content = message.get('content')
        mode = message.get('mode', 'normal')
        msg_type = message.get('message_type', 'text')

        # 检查是否被拉黑
        self.cursor.execute(
            "SELECT id FROM blacklist WHERE user_uuid = ? AND blocked_uuid = ?",
            (receiver_uuid, sender_uuid)
        )
        if self.cursor.fetchone():
            return {'success': False, 'error': '你已被对方拉黑', 'type': 'send_message_response'}

        # 检查是否被禁言（如果是群消息）
        if receiver_uuid.startswith('Z'):  # 群组UUID以Z开头
            try:
                self.cursor.execute(
                    "SELECT is_muted FROM group_members WHERE group_uuid = ? AND member_uuid = ?",
                    (receiver_uuid, sender_uuid)
                )
                result = self.cursor.fetchone()
                if result and result[0]:
                    return {'success': False, 'error': '你已被禁言', 'type': 'send_message_response'}
            except sqlite3.OperationalError:
                # is_muted列可能不存在，忽略禁言检查
                pass

        # 保存消息到数据库
        self.cursor.execute(
            "INSERT INTO messages (sender_uuid, receiver_uuid, message_type, content, mode) VALUES (?, ?, ?, ?, ?)",
            (sender_uuid, receiver_uuid, msg_type, content, mode)
        )
        self.conn.commit()

        # 如果接收方在线，发送消息
        if receiver_uuid in self.clients:
            try:
                message_data = {
                    'type': 'new_message',
                    'sender_uuid': sender_uuid,
                    'content': content,
                    'mode': mode,
                    'message_type': msg_type,
                    'timestamp': datetime.now().isoformat()
                }
                self.clients[receiver_uuid].send(json.dumps(message_data).encode('utf-8'))
            except Exception as e:
                logging.error(f"发送消息错误: {e}")
                # 如果发送失败，保存为离线消息
                self.save_offline_message(receiver_uuid, message_data)
        else:
            # 接收方不在线，保存为离线消息
            message_data = {
                'type': 'new_message',
                'sender_uuid': sender_uuid,
                'content': content,
                'mode': mode,
                'message_type': msg_type,
                'timestamp': datetime.now().isoformat()
            }
            self.save_offline_message(receiver_uuid, message_data)

        return {'success': True, 'message': '消息发送成功', 'type': 'send_message_response'}

    def save_offline_message(self, user_uuid, message_data):
        """保存离线消息"""
        try:
            self.cursor.execute(
                "INSERT INTO offline_messages (user_uuid, message_data) VALUES (?, ?)",
                (user_uuid, json.dumps(message_data))
            )
            self.conn.commit()
        except Exception as e:
            logging.error(f"保存离线消息错误: {e}")

    def send_offline_messages(self, user_uuid, client_socket):
        """发送离线消息给用户"""
        try:
            self.cursor.execute(
                "SELECT id, message_data FROM offline_messages WHERE user_uuid = ?",
                (user_uuid,)
            )
            offline_messages = self.cursor.fetchall()

            for msg_id, msg_data in offline_messages:
                try:
                    client_socket.send(msg_data.encode('utf-8'))
                    # 删除已发送的离线消息
                    self.cursor.execute(
                        "DELETE FROM offline_messages WHERE id = ?",
                        (msg_id,)
                    )
                except Exception as e:
                    logging.error(f"发送离线消息错误: {e}")

            self.conn.commit()
        except Exception as e:
            logging.error(f"获取离线消息错误: {e}")

    def get_offline_messages(self, message):
        """获取离线消息"""
        user_uuid = message.get('user_uuid')
        try:
            self.cursor.execute(
                "SELECT message_data FROM offline_messages WHERE user_uuid = ?",
                (user_uuid,)
            )
            messages = [json.loads(row[0]) for row in self.cursor.fetchall()]
            return {'success': True, 'messages': messages, 'type': 'offline_messages'}
        except Exception as e:
            logging.error(f"获取离线消息错误: {e}")
            return {'success': False, 'error': '获取离线消息失败', 'type': 'error_response'}

    def delete_offline_messages(self, message):
        """删除离线消息"""
        user_uuid = message.get('user_uuid')
        try:
            self.cursor.execute(
                "DELETE FROM offline_messages WHERE user_uuid = ?",
                (user_uuid,)
            )
            self.conn.commit()
            return {'success': True, 'type': 'delete_offline_messages_response'}
        except Exception as e:
            logging.error(f"删除离线消息错误: {e}")
            return {'success': False, 'error': '删除离线消息失败', 'type': 'error_response'}

    def get_friends(self, message):
        user_uuid = message.get('user_uuid')

        try:
            # 尝试包含avatar的查询
            self.cursor.execute('''
                SELECT u.uuid, u.username, u.avatar 
                FROM friends f 
                JOIN users u ON f.friend_uuid = u.uuid 
                WHERE f.user_uuid = ? AND f.status = 'accepted'
            ''', (user_uuid,))
        except sqlite3.OperationalError:
            # 如果avatar列不存在，使用旧的查询
            self.cursor.execute('''
                SELECT u.uuid, u.username 
                FROM friends f 
                JOIN users u ON f.friend_uuid = u.uuid 
                WHERE f.user_uuid = ? AND f.status = 'accepted'
            ''', (user_uuid,))

        friends = []
        for row in self.cursor.fetchall():
            if len(row) == 3:  # 包含avatar的查询结果
                avatar_base64 = None
                if row[2]:
                    avatar_base64 = base64.b64encode(row[2]).decode('utf-8')
                friends.append({'uuid': row[0], 'username': row[1], 'avatar': avatar_base64})
            else:  # 不包含avatar的查询结果
                friends.append({'uuid': row[0], 'username': row[1], 'avatar': None})

        return {'success': True, 'friends': friends, 'type': 'friends_list'}

    def add_friend(self, message):
        user_uuid = message.get('user_uuid')
        friend_username = message.get('friend_username')

        # 查找用户
        self.cursor.execute("SELECT uuid, username FROM users WHERE username = ?", (friend_username,))
        friend = self.cursor.fetchone()

        if not friend:
            return {'success': False, 'error': '用户不存在', 'type': 'add_friend_response'}

        friend_uuid = friend[0]

        # 检查是否已经是好友
        self.cursor.execute(
            "SELECT id FROM friends WHERE user_uuid = ? AND friend_uuid = ?",
            (user_uuid, friend_uuid)
        )
        if self.cursor.fetchone():
            return {'success': False, 'error': '已经是好友', 'type': 'add_friend_response'}

        # 添加好友
        self.cursor.execute(
            "INSERT INTO friends (user_uuid, friend_uuid) VALUES (?, ?)",
            (user_uuid, friend_uuid)
        )
        self.conn.commit()

        return {'success': True, 'message': '好友添加成功', 'type': 'add_friend_response'}

    def add_friend_response(self, message):
        """处理好友请求响应"""
        user_uuid = message.get('user_uuid')
        friend_uuid = message.get('friend_uuid')
        accepted = message.get('accepted', False)

        if accepted:
            # 更新好友状态为已接受
            self.cursor.execute(
                "UPDATE friends SET status = 'accepted' WHERE user_uuid = ? AND friend_uuid = ?",
                (user_uuid, friend_uuid)
            )
            self.conn.commit()
            return {'success': True, 'message': '好友请求已接受', 'type': 'add_friend_response'}
        else:
            # 删除好友请求
            self.cursor.execute(
                "DELETE FROM friends WHERE user_uuid = ? AND friend_uuid = ?",
                (user_uuid, friend_uuid)
            )
            self.conn.commit()
            return {'success': True, 'message': '好友请求已拒绝', 'type': 'add_friend_response'}

    def block_user(self, message):
        user_uuid = message.get('user_uuid')
        block_username = message.get('block_username')

        # 查找用户
        self.cursor.execute("SELECT uuid FROM users WHERE username = ?", (block_username,))
        block_user = self.cursor.fetchone()

        if not block_user:
            return {'success': False, 'error': '用户不存在', 'type': 'block_user_response'}

        block_uuid = block_user[0]

        # 检查是否已经拉黑
        self.cursor.execute(
            "SELECT id FROM blacklist WHERE user_uuid = ? AND blocked_uuid = ?",
            (user_uuid, block_uuid)
        )
        if self.cursor.fetchone():
            return {'success': False, 'error': '已经拉黑该用户', 'type': 'block_user_response'}

        # 拉黑用户
        self.cursor.execute(
            "INSERT INTO blacklist (user_uuid, blocked_uuid) VALUES (?, ?)",
            (user_uuid, block_uuid)
        )
        self.conn.commit()

        return {'success': True, 'message': '拉黑成功', 'type': 'block_user_response'}

    def create_group(self, message):
        user_uuid = message.get('user_uuid')
        group_name = message.get('group_name')

        group_uuid = "Z" + str(uuid.uuid4())[1:]  # 群组UUID以Z开头

        self.cursor.execute(
            "INSERT INTO groups (uuid, name, owner_uuid) VALUES (?, ?, ?)",
            (group_uuid, group_name, user_uuid)
        )

        self.cursor.execute(
            "INSERT INTO group_members (group_uuid, member_uuid, role) VALUES (?, ?, ?)",
            (group_uuid, user_uuid, 'owner')
        )

        self.conn.commit()

        return {'success': True, 'message': '群聊创建成功', 'group_uuid': group_uuid, 'type': 'create_group_response'}

    def get_groups(self, message):
        user_uuid = message.get('user_uuid')

        try:
            # 尝试包含所有列的查询
            self.cursor.execute('''
                SELECT g.uuid, g.name, gm.role, gm.custom_nickname, gm.title
                FROM group_members gm 
                JOIN groups g ON gm.group_uuid = g.uuid 
                WHERE gm.member_uuid = ?
            ''', (user_uuid,))

            groups = []
            for row in self.cursor.fetchall():
                groups.append({
                    'uuid': row[0],
                    'name': row[1],
                    'role': row[2],
                    'custom_nickname': row[3],
                    'title': row[4]
                })
        except sqlite3.OperationalError:
            # 如果某些列不存在，使用基本查询
            self.cursor.execute('''
                SELECT g.uuid, g.name, gm.role
                FROM group_members gm 
                JOIN groups g ON gm.group_uuid = g.uuid 
                WHERE gm.member_uuid = ?
            ''', (user_uuid,))

            groups = []
            for row in self.cursor.fetchall():
                groups.append({
                    'uuid': row[0],
                    'name': row[1],
                    'role': row[2],
                    'custom_nickname': '',
                    'title': ''
                })

        return {'success': True, 'groups': groups, 'type': 'groups_list'}

    def join_group(self, message):
        user_uuid = message.get('user_uuid')
        group_uuid = message.get('group_uuid')

        # 检查是否已经是群成员
        self.cursor.execute(
            "SELECT id FROM group_members WHERE group_uuid = ? AND member_uuid = ?",
            (group_uuid, user_uuid)
        )
        if self.cursor.fetchone():
            return {'success': False, 'error': '已经是群成员', 'type': 'join_group_response'}

        # 加入群聊
        self.cursor.execute(
            "INSERT INTO group_members (group_uuid, member_uuid) VALUES (?, ?)",
            (group_uuid, user_uuid)
        )
        self.conn.commit()

        return {'success': True, 'message': '加入群聊成功', 'type': 'join_group_response'}

    def upload_emoji(self, message):
        user_uuid = message.get('user_uuid')
        emoji_name = message.get('emoji_name')
        image_data = message.get('image_data')

        # 解码图片数据
        try:
            image_bytes = base64.b64decode(image_data)

            # 检查图片大小
            if len(image_bytes) > MAX_FILE_SIZE:
                return {'success': False, 'error': '图片大小超过限制', 'type': 'upload_emoji_response'}

            emoji_uuid = str(uuid.uuid4())

            self.cursor.execute(
                "INSERT INTO emojis (uuid, name, image_data, uploader_uuid) VALUES (?, ?, ?, ?)",
                (emoji_uuid, emoji_name, image_bytes, user_uuid)
            )
            self.conn.commit()

            return {'success': True, 'message': '表情上传成功', 'emoji_uuid': emoji_uuid,
                    'type': 'upload_emoji_response'}
        except Exception as e:
            logging.error(f"上传表情错误: {e}")
            return {'success': False, 'error': '上传失败', 'type': 'upload_emoji_response'}

    def get_emojis(self, message):
        self.cursor.execute("SELECT uuid, name FROM emojis")
        emojis = [{'uuid': row[0], 'name': row[1]} for row in self.cursor.fetchall()]
        return {'success': True, 'emojis': emojis, 'type': 'emojis_list'}

    def get_user_info(self, message):
        username = message.get('username')

        try:
            self.cursor.execute("SELECT uuid, username, avatar FROM users WHERE username = ?", (username,))
            user = self.cursor.fetchone()

            if user:
                avatar_base64 = None
                if user[2]:
                    avatar_base64 = base64.b64encode(user[2]).decode('utf-8')
                return {'success': True, 'user': {'uuid': user[0], 'username': user[1], 'avatar': avatar_base64},
                        'type': 'user_info_response'}
            else:
                return {'success': False, 'error': '用户不存在', 'type': 'user_info_response'}
        except sqlite3.OperationalError:
            # 如果avatar列不存在，使用基本查询
            self.cursor.execute("SELECT uuid, username FROM users WHERE username = ?", (username,))
            user = self.cursor.fetchone()

            if user:
                return {'success': True, 'user': {'uuid': user[0], 'username': user[1], 'avatar': None},
                        'type': 'user_info_response'}
            else:
                return {'success': False, 'error': '用户不存在', 'type': 'user_info_response'}

    def update_profile(self, message):
        user_uuid = message.get('user_uuid')
        new_username = message.get('new_username')
        avatar_data = message.get('avatar_data')

        try:
            if new_username:
                # 检查用户名是否已存在
                self.cursor.execute("SELECT id FROM users WHERE username = ? AND uuid != ?", (new_username, user_uuid))
                if self.cursor.fetchone():
                    return {'success': False, 'error': '用户名已存在', 'type': 'update_profile_response'}

                self.cursor.execute(
                    "UPDATE users SET username = ? WHERE uuid = ?",
                    (new_username, user_uuid)
                )

            if avatar_data:
                try:
                    avatar_bytes = base64.b64decode(avatar_data)
                    self.cursor.execute(
                        "UPDATE users SET avatar = ? WHERE uuid = ?",
                        (avatar_bytes, user_uuid)
                    )
                except sqlite3.OperationalError:
                    # avatar列可能不存在，忽略错误
                    logging.warning("avatar列不存在，无法更新头像")

            self.conn.commit()
            return {'success': True, 'message': '资料更新成功', 'type': 'update_profile_response'}
        except Exception as e:
            logging.error(f"更新资料错误: {e}")
            return {'success': False, 'error': '资料更新失败', 'type': 'update_profile_response'}

    def download_chat_history(self, message):
        user_uuid = message.get('user_uuid')
        target_uuid = message.get('target_uuid')
        chat_type = message.get('chat_type')  # 'user' 或 'group'

        try:
            if chat_type == 'user':
                # 获取与特定用户的聊天记录
                self.cursor.execute('''
                    SELECT sender_uuid, content, message_type, mode, timestamp 
                    FROM messages 
                    WHERE (sender_uuid = ? AND receiver_uuid = ?) 
                    OR (sender_uuid = ? AND receiver_uuid = ?)
                    ORDER BY timestamp
                ''', (user_uuid, target_uuid, target_uuid, user_uuid))
            else:
                # 获取群组聊天记录
                self.cursor.execute('''
                    SELECT sender_uuid, content, message_type, mode, timestamp 
                    FROM messages 
                    WHERE receiver_uuid = ?
                    ORDER BY timestamp
                ''', (target_uuid,))

            messages = []
            for row in self.cursor.fetchall():
                messages.append({
                    'sender_uuid': row[0],
                    'content': row[1],
                    'message_type': row[2],
                    'mode': row[3],
                    'timestamp': row[4]
                })

            return {'success': True, 'messages': messages, 'type': 'chat_history_response'}
        except Exception as e:
            logging.error(f"获取聊天记录错误: {e}")
            return {'success': False, 'error': '获取聊天记录失败', 'type': 'chat_history_response'}

    def group_management(self, message):
        action = message.get('action')
        group_uuid = message.get('group_uuid')
        user_uuid = message.get('user_uuid')
        target_uuid = message.get('target_uuid')
        extra_data = message.get('extra_data')  # 用于传递标题、昵称等信息

        try:
            # 检查用户是否有权限
            self.cursor.execute(
                "SELECT role FROM group_members WHERE group_uuid = ? AND member_uuid = ?",
                (group_uuid, user_uuid)
            )
            user_role = self.cursor.fetchone()

            if not user_role or (user_role[0] not in ['owner', 'admin'] and action != 'set_nickname'):
                return {'success': False, 'error': '权限不足', 'type': 'group_management_response'}

            if action == 'set_admin':
                # 设置管理员
                self.cursor.execute(
                    "UPDATE group_members SET role = 'admin' WHERE group_uuid = ? AND member_uuid = ?",
                    (group_uuid, target_uuid)
                )
                message_text = '设置管理员成功'

            elif action == 'remove_admin':
                # 撤职管理员
                self.cursor.execute(
                    "UPDATE group_members SET role = 'member' WHERE group_uuid = ? AND member_uuid = ?",
                    (group_uuid, target_uuid)
                )
                message_text = '撤职管理员成功'

            elif action == 'mute_member':
                # 禁言成员
                try:
                    self.cursor.execute(
                        "UPDATE group_members SET is_muted = 1 WHERE group_uuid = ? AND member_uuid = ?",
                        (group_uuid, target_uuid)
                    )
                    message_text = '禁言成功'
                except sqlite3.OperationalError:
                    return {'success': False, 'error': '禁言功能不可用', 'type': 'group_management_response'}

            elif action == 'unmute_member':
                # 解除禁言
                try:
                    self.cursor.execute(
                        "UPDATE group_members SET is_muted = 0 WHERE group_uuid = ? AND member_uuid = ?",
                        (group_uuid, target_uuid)
                    )
                    message_text = '解除禁言成功'
                except sqlite3.OperationalError:
                    return {'success': False, 'error': '禁言功能不可用', 'type': 'group_management_response'}

            elif action == 'set_title':
                # 设置头衔
                try:
                    self.cursor.execute(
                        "UPDATE group_members SET title = ? WHERE group_uuid = ? AND member_uuid = ?",
                        (extra_data, group_uuid, target_uuid)
                    )
                    message_text = '设置头衔成功'
                except sqlite3.OperationalError:
                    return {'success': False, 'error': '头衔功能不可用', 'type': 'group_management_response'}

            elif action == 'set_nickname':
                # 设置群昵称（用户自己设置）
                try:
                    self.cursor.execute(
                        "UPDATE group_members SET custom_nickname = ? WHERE group_uuid = ? AND member_uuid = ?",
                        (extra_data, group_uuid, user_uuid)
                    )
                    message_text = '设置群昵称成功'
                except sqlite3.OperationalError:
                    return {'success': False, 'error': '群昵称功能不可用', 'type': 'group_management_response'}

            else:
                return {'success': False, 'error': '未知的操作', 'type': 'group_management_response'}

            self.conn.commit()
            return {'success': True, 'message': message_text, 'type': 'group_management_response'}

        except Exception as e:
            logging.error(f"群管理错误: {e}")
            return {'success': False, 'error': '操作失败', 'type': 'group_management_response'}

    def search_users_groups(self, message):
        query = message.get('query', '')

        try:
            # 搜索用户
            try:
                self.cursor.execute(
                    "SELECT uuid, username, avatar FROM users WHERE username LIKE ?",
                    (f'%{query}%',)
                )
                users = []
                for row in self.cursor.fetchall():
                    avatar_base64 = None
                    if row[2]:
                        avatar_base64 = base64.b64encode(row[2]).decode('utf-8')
                    users.append({'uuid': row[0], 'username': row[1], 'avatar': avatar_base64})
            except sqlite3.OperationalError:
                # 如果avatar列不存在，使用基本查询
                self.cursor.execute(
                    "SELECT uuid, username FROM users WHERE username LIKE ?",
                    (f'%{query}%',)
                )
                users = [{'uuid': row[0], 'username': row[1], 'avatar': None} for row in self.cursor.fetchall()]

            # 搜索群组
            self.cursor.execute(
                "SELECT uuid, name FROM groups WHERE name LIKE ?",
                (f'%{query}%',)
            )
            groups = [{'uuid': row[0], 'name': row[1]} for row in self.cursor.fetchall()]

            return {'success': True, 'users': users, 'groups': groups, 'type': 'search_response'}
        except Exception as e:
            logging.error(f"搜索错误: {e}")
            return {'success': False, 'error': '搜索失败', 'type': 'search_response'}

    def start_call(self, message):
        call_type = message.get('call_type')  # 'voice', 'video', 'screen'
        target_uuid = message.get('target_uuid')
        caller_uuid = message.get('caller_uuid')

        # 通知目标用户有来电
        if target_uuid in self.clients:
            try:
                self.clients[target_uuid].send(json.dumps({
                    'type': 'incoming_call',
                    'call_type': call_type,
                    'caller_uuid': caller_uuid,
                    'timestamp': datetime.now().isoformat()
                }).encode('utf-8'))
                return {'success': True, 'message': '呼叫已发送', 'type': 'call_response'}
            except Exception as e:
                logging.error(f"发送呼叫通知错误: {e}")
                return {'success': False, 'error': '呼叫失败', 'type': 'call_response'}
        else:
            return {'success': False, 'error': '用户不在线', 'type': 'call_response'}

    def start(self):
        logging.info("服务器正在运行...")
        try:
            while self.running:
                try:
                    # 设置超时以便定期检查运行状态
                    self.server_socket.settimeout(1.0)
                    client_socket, address = self.server_socket.accept()
                    client_thread = threading.Thread(target=self.handle_client, args=(client_socket, address))
                    client_thread.daemon = True
                    client_thread.start()
                except socket.timeout:
                    # 超时是正常的，继续循环检查运行状态
                    continue
                except Exception as e:
                    if self.running:  # 只有在服务器运行时才记录错误
                        logging.error(f"接受连接错误: {e}")
        except KeyboardInterrupt:
            logging.info("接收到键盘中断，正在关闭服务器...")
        finally:
            self.running = False
            self.server_socket.close()
            self.conn.close()
            logging.info("服务器已关闭")





class VideoCall:
    def __init__(self, root, call_type, is_caller=True, target_name="对方"):
        self.root = root
        self.call_type = call_type
        self.is_caller = is_caller
        self.target_name = target_name
        self.is_active = True
        self.cap = None
        self.screen_cap = None
        self.audio_stream = None

        # 初始化pygame音频
        pygame.init()
        mixer.init()

        self.setup_ui()

        if is_caller:
            self.start_call()
        else:
            self.accept_call()

    def setup_ui(self):
        self.window = tk.Toplevel(self.root)
        self.window.title(f"{'拨号给' if self.is_caller else '来自'} {self.target_name} - {self.call_type}通话")
        self.window.geometry("800x600")
        self.window.protocol("WM_DELETE_WINDOW", self.end_call)

        # 视频显示区域
        self.video_frame = ttk.Frame(self.window)
        self.video_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        self.video_label = ttk.Label(self.video_frame)
        self.video_label.pack(fill=tk.BOTH, expand=True)

        # 控制按钮区域
        control_frame = ttk.Frame(self.window)
        control_frame.pack(fill=tk.X, padx=10, pady=5)

        self.mute_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(control_frame, text="静音", variable=self.mute_var,
                        command=self.toggle_mute).pack(side=tk.LEFT, padx=5)

        self.video_var = tk.BooleanVar(value=True)
        if self.call_type in ['video', 'screen']:
            ttk.Checkbutton(control_frame, text="关闭视频", variable=self.video_var,
                            command=self.toggle_video).pack(side=tk.LEFT, padx=5)

        ttk.Button(control_frame, text="结束通话", command=self.end_call).pack(side=tk.RIGHT, padx=5)

        # 状态标签
        self.status_var = tk.StringVar(value="通话中...")
        status_label = ttk.Label(self.window, textvariable=self.status_var)
        status_label.pack(pady=5)

    def start_call(self):
        if self.call_type in ['video', 'screen']:
            self.start_video()
        if self.call_type in ['voice', 'video']:
            self.start_audio()

    def accept_call(self):
        if self.call_type in ['video', 'screen']:
            self.start_video()
        if self.call_type in ['voice', 'video']:
            self.start_audio()

    def start_video(self):
        if self.call_type == 'screen':
            # 屏幕共享
            try:
                # 获取屏幕尺寸
                screen = screeninfo.get_monitors()[0]
                self.screen_cap = {"width": screen.width, "height": screen.height}
                self.update_screen_preview()
            except Exception as e:
                logging.error(f"屏幕共享错误: {e}")
                self.status_var.set("屏幕共享失败")
        else:
            # 摄像头视频
            try:
                self.cap = cv2.VideoCapture(0)
                if not self.cap.isOpened():
                    self.status_var.set("无法打开摄像头")
                    return

                self.update_video_preview()
            except Exception as e:
                logging.error(f"摄像头错误: {e}")
                self.status_var.set("摄像头启动失败")

    def start_audio(self):
        try:
            # 初始化音频流
            self.audio_stream = mixer.Sound(buffer=bytearray(1024))
            self.audio_stream.play(loops=-1)
        except Exception as e:
            logging.error(f"音频启动错误: {e}")
            self.status_var.set("音频启动失败")

    def update_video_preview(self):
        if self.cap and self.is_active and self.video_var.get():
            ret, frame = self.cap.read()
            if ret:
                # 转换颜色空间 BGR -> RGB
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                # 调整大小
                frame = cv2.resize(frame, (640, 480))
                # 转换为PIL图像
                img = Image.fromarray(frame)
                # 转换为Tkinter图像
                imgtk = ImageTk.PhotoImage(image=img)
                # 更新标签
                self.video_label.imgtk = imgtk
                self.video_label.configure(image=imgtk)

        if self.is_active:
            self.window.after(30, self.update_video_preview)

    def update_screen_preview(self):
        if self.is_active and self.video_var.get():
            try:
                # 使用PIL获取屏幕截图
                from PIL import ImageGrab
                screenshot = ImageGrab.grab()
                # 调整大小
                screenshot = screenshot.resize((640, 480))
                # 转换为Tkinter图像
                imgtk = ImageTk.PhotoImage(image=screenshot)
                # 更新标签
                self.video_label.imgtk = imgtk
                self.video_label.configure(image=imgtk)
            except Exception as e:
                logging.error(f"屏幕截图错误: {e}")

        if self.is_active:
            self.window.after(100, self.update_screen_preview)  # 降低屏幕共享的更新频率

    def toggle_mute(self):
        if self.audio_stream:
            if self.mute_var.get():
                self.audio_stream.set_volume(0)
            else:
                self.audio_stream.set_volume(1)

    def toggle_video(self):
        if self.call_type in ['video', 'screen']:
            if self.video_var.get():
                self.status_var.set("视频已开启")
            else:
                self.status_var.set("视频已关闭")

    def end_call(self):
        self.is_active = False
        if self.cap:
            self.cap.release()
        if self.audio_stream:
            self.audio_stream.stop()
        self.window.destroy()


class ChatClient:
    def __init__(self, root):

        self.root = root
        self.root.title("聊天客户端")
        self.root.geometry("1000x700")

        # 加载配置
        self.config = configparser.ConfigParser()
        self.config_file = 'client.ini'
        self.load_config()

        # 连接状态
        self.connected = False
        self.socket = None
        self.user = None

        # 好友列表和群组列表
        self.friends = []
        self.groups = []
        self.emojis = []

        # 当前聊天对象
        self.current_chat = None
        self.current_chat_type = None  # 'user' 或 'group'

        # 通话状态
        self.active_call = None

        # 创建界面
        self.create_widgets()

        # 尝试连接服务器
        self.connect_to_server()

        # 绑定快捷键
        self.bind_shortcuts()

        # 尝试自动登录
        self.auto_login()

    def load_config(self):
        if os.path.exists(self.config_file):
            self.config.read(self.config_file)
        else:
            # 创建默认配置
            self.config['SERVER'] = {'ip': SERVER_IP, 'port': str(SERVER_PORT)}
            self.config['AUTH'] = {'username': '', 'password': '', 'auto_login': 'False'}
            self.save_config()

    def save_config(self):
        with open(self.config_file, 'w') as f:
            self.config.write(f)

    def auto_login(self):
        if self.config.getboolean('AUTH', 'auto_login', fallback=False):
            username = self.config.get('AUTH', 'username', fallback='')
            password = self.config.get('AUTH', 'password', fallback='')

            if username and password:
                self.send_to_server({
                    'type': 'login',
                    'username': username,
                    'password': password
                })

    def create_widgets(self):
        # 主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 配置行权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(1, weight=1)

        # 顶部按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))

        ttk.Button(button_frame, text="登录", command=self.show_login_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="注册", command=self.show_register_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="添加好友", command=self.show_add_friend_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="创建群聊", command=self.show_create_group_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="拉黑用户", command=self.show_block_user_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="刷新", command=self.refresh_lists).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="个人资料", command=self.show_user_profile_dialog).pack(side=tk.LEFT, padx=(0, 5))

        # 搜索框
        search_frame = ttk.Frame(button_frame)
        search_frame.pack(side=tk.LEFT, padx=(20, 0))
        ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT)
        self.search_var = tk.StringVar()
        search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=15)
        search_entry.pack(side=tk.LEFT, padx=(5, 0))
        search_entry.bind('<Return>', lambda e: self.search_users_groups())

        # 左侧联系人框架
        left_frame = ttk.LabelFrame(main_frame, text="联系人", padding="5")
        left_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10))
        left_frame.columnconfigure(0, weight=1)
        left_frame.rowconfigure(1, weight=1)

        # 好友列表
        friend_frame = ttk.Frame(left_frame)
        friend_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        friend_frame.columnconfigure(0, weight=1)
        ttk.Label(friend_frame, text="好友列表").grid(row=0, column=0, sticky=tk.W)

        self.friends_listbox = tk.Listbox(friend_frame, height=15)
        self.friends_listbox.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(5, 10))
        self.friends_listbox.bind('<<ListboxSelect>>', self.on_friend_select)
        self.friends_listbox.bind('<Button-3>', self.show_friend_context_menu)

        # 群组列表
        group_frame = ttk.Frame(left_frame)
        group_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        group_frame.columnconfigure(0, weight=1)
        group_frame.rowconfigure(1, weight=1)
        ttk.Label(group_frame, text="群组列表").grid(row=0, column=0, sticky=tk.W, pady=(10, 0))

        self.groups_listbox = tk.Listbox(group_frame, height=10)
        self.groups_listbox.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(5, 0))
        self.groups_listbox.bind('<<ListboxSelect>>', self.on_group_select)
        self.groups_listbox.bind('<Button-3>', self.show_group_context_menu)

        # 右侧聊天框架
        right_frame = ttk.Frame(main_frame)
        right_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S))
        right_frame.columnconfigure(0, weight=1)
        right_frame.rowconfigure(1, weight=1)

        # 聊天标题和功能按钮
        title_frame = ttk.Frame(right_frame)
        title_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5))
        title_frame.columnconfigure(0, weight=1)

        self.chat_title = ttk.Label(title_frame, text="未选择聊天", font=('Arial', 12, 'bold'))
        self.chat_title.grid(row=0, column=0, sticky=tk.W)

        ttk.Button(title_frame, text="下载聊天记录", command=self.download_chat_history).grid(row=0, column=1,
                                                                                              padx=(5, 0))
        ttk.Button(title_frame, text="语音通话", command=lambda: self.start_call('voice')).grid(row=0, column=2,
                                                                                                padx=(5, 0))
        ttk.Button(title_frame, text="视频通话", command=lambda: self.start_call('video')).grid(row=0, column=3,
                                                                                                padx=(5, 0))
        ttk.Button(title_frame, text="屏幕共享", command=lambda: self.start_call('screen')).grid(row=0, column=4,
                                                                                                 padx=(5, 0))

        # 聊天区域
        chat_frame = ttk.Frame(right_frame)
        chat_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        chat_frame.columnconfigure(0, weight=1)
        chat_frame.rowconfigure(0, weight=1)

        self.chat_area = scrolledtext.ScrolledText(chat_frame, wrap=tk.WORD, state=tk.DISABLED)
        self.chat_area.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 输入区域
        input_frame = ttk.Frame(right_frame)
        input_frame.grid(row=2, column=0, sticky=(tk.W, tk.E), pady=(5, 0))
        input_frame.columnconfigure(0, weight=1)

        # 模式选择
        mode_frame = ttk.Frame(input_frame)
        mode_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), pady=(0, 5))

        ttk.Label(mode_frame, text="模式:").pack(side=tk.LEFT)
        self.mode_var = tk.StringVar(value="normal")
        ttk.Combobox(mode_frame, textvariable=self.mode_var,
                     values=["normal", "markdown", "html"], state="readonly", width=10).pack(side=tk.LEFT, padx=(5, 0))

        # 表情按钮
        ttk.Button(mode_frame, text="表情", command=self.show_emoji_dialog).pack(side=tk.RIGHT, padx=(5, 0))

        # 文件按钮
        ttk.Button(mode_frame, text="文件", command=self.send_file).pack(side=tk.RIGHT, padx=(5, 0))

        # 输入框和发送按钮
        bottom_frame = ttk.Frame(input_frame)
        bottom_frame.grid(row=1, column=0, sticky=(tk.W, tk.E))
        bottom_frame.columnconfigure(0, weight=1)

        self.input_field = tk.Text(bottom_frame, height=3, wrap=tk.WORD)
        self.input_field.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 5))

        ttk.Button(bottom_frame, text="发送", command=self.send_message).grid(row=0, column=1, sticky=(tk.E, tk.S))

        # 状态栏
        self.status_var = tk.StringVar(value="未连接")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=2, column=0, sticky=(tk.W, tk.E))

    def bind_shortcuts(self):
        self.root.bind('<Control-Return>', lambda e: self.send_message())
        self.root.bind('<Control-f>', lambda e: self.search_users_groups())

    def diagnose_connection(self):
        """诊断连接问题"""
        server_ip = self.config.get('SERVER', 'ip', fallback=SERVER_IP)
        server_port = self.config.getint('SERVER', 'port', fallback=SERVER_PORT)

        results = []

        # 1. 检查网络连通性
        try:
            # 使用ping检查基本连通性
            param = "-n" if platform.system().lower() == "windows" else "-c"
            command = ["ping", param, "1", server_ip]
            result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=5)
            if result.returncode == 0:
                results.append(f"✓ 可以ping通服务器 {server_ip}")
            else:
                results.append(f"✗ 无法ping通服务器 {server_ip}")
        except:
            results.append(f"✗ ping测试失败")

        # 2. 检查端口是否开放
        try:
            test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            test_socket.settimeout(5)
            result = test_socket.connect_ex((server_ip, server_port))
            test_socket.close()
            if result == 0:
                results.append(f"✓ 端口 {server_port} 是开放的")
            else:
                results.append(f"✗ 端口 {server_port} 是关闭的 (错误代码: {result})")
        except Exception as e:
            results.append(f"✗ 端口检查失败: {e}")

        # 3. 显示诊断结果
        dialog = tk.Toplevel(self.root)
        dialog.title("连接诊断")
        dialog.geometry("500x300")

        text_area = scrolledtext.ScrolledText(dialog, wrap=tk.WORD)
        text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        for result in results:
            text_area.insert(tk.END, result + "\n")

        text_area.config(state=tk.DISABLED)

        ttk.Button(dialog, text="关闭", command=dialog.destroy).pack(pady=10)

    def connect_to_server(self):
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_ip = self.config.get('SERVER', 'ip', fallback=SERVER_IP)
            server_port = self.config.getint('SERVER', 'port', fallback=SERVER_PORT)
            self.socket.connect((server_ip, server_port))
            self.connected = True
            self.status_var.set("已连接到服务器")

            # 版本检测
            self.send_to_server({
                'type': 'version_check',
                'version': CLIENT_VERSION
            })

            # 启动接收线程
            receive_thread = threading.Thread(target=self.receive_messages)
            receive_thread.daemon = True
            receive_thread.start()

        except Exception as e:
            self.diagnose_connection()
            self.status_var.set(f"连接失败: {str(e)}")
            logging.error(f"连接服务器错误: {e}")


    def receive_messages(self):
        while self.connected:
            try:
                data = self.socket.recv(BUFFER_SIZE).decode('utf-8')
                if not data:
                    self.connected = False
                    self.status_var.set("与服务器的连接已断开")
                    break

                message = json.loads(data)
                self.handle_server_message(message)

            except Exception as e:
                logging.error(f"接收消息错误: {e}")
                self.connected = False
                self.status_var.set("与服务器的连接已断开")
                break

    def handle_server_message(self, message):
        msg_type = message.get('type')

        if msg_type == 'new_message':
            self.display_message(
                message.get('sender_uuid'),
                message.get('content'),
                message.get('mode', 'normal'),
                message.get('message_type', 'text'),
                message.get('timestamp')
            )
            # 保存消息到本地
            self.save_message_to_local(message)
        elif msg_type == 'login_response':
            if message.get('success'):
                self.user = message['user']
                self.status_var.set(f"已登录为 {self.user['username']}")

                # 保存登录信息
                self.config['AUTH']['username'] = self.user['username']
                if self.config.getboolean('AUTH', 'auto_login', fallback=False):
                    # 密码已保存，不需要再次保存
                    pass
                self.save_config()

                self.refresh_lists()

                # 获取离线消息
                self.get_offline_messages()
            else:
                messagebox.showerror("登录失败", message.get('error', '未知错误'))
        elif msg_type == 'register_response':
            if message.get('success'):
                messagebox.showinfo("注册成功", message.get('message', '注册成功'))
            else:
                messagebox.showerror("注册失败", message.get('error', '未知错误'))
        elif msg_type == 'friends_list':
            self.friends = message.get('friends', [])
            self.update_friends_list()
        elif msg_type == 'groups_list':
            self.groups = message.get('groups', [])
            self.update_groups_list()
        elif msg_type == 'emojis_list':
            self.emojis = message.get('emojis', [])
        elif msg_type == 'user_info_response':
            if message.get('success'):
                # 处理用户信息
                user_info = message.get('user')
                self.show_user_info_dialog(user_info)
            else:
                messagebox.showerror("错误", message.get('error', '未知错误'))
        elif msg_type == 'update_profile_response':
            if message.get('success'):
                messagebox.showinfo("成功", message.get('message', '资料更新成功'))
                # 更新本地用户信息
                if 'new_username' in message:
                    self.user['username'] = message['new_username']
                    self.status_var.set(f"已登录为 {self.user['username']}")
            else:
                messagebox.showerror("错误", message.get('error', '资料更新失败'))
        elif msg_type == 'chat_history_response':
            if message.get('success'):
                self.save_chat_history(message.get('messages', []))
            else:
                messagebox.showerror("错误", message.get('error', '下载聊天记录失败'))
        elif msg_type == 'group_management_response':
            if message.get('success'):
                messagebox.showinfo("成功", message.get('message', '操作成功'))
                self.refresh_lists()
            else:
                messagebox.showerror("错误", message.get('error', '操作失败'))
        elif msg_type == 'search_response':
            if message.get('success'):
                self.show_search_results(
                    message.get('users', []),
                    message.get('groups', [])
                )
            else:
                messagebox.showerror("错误", message.get('error', '搜索失败'))
        elif msg_type == 'incoming_call':
            # 处理来电
            self.handle_incoming_call(message)
        elif msg_type == 'call_response':
            if message.get('success'):
                messagebox.showinfo("成功", message.get('message', '呼叫已发送'))
            else:
                messagebox.showerror("错误", message.get('error', '呼叫失败'))
        elif msg_type == 'offline_messages':
            if message.get('success'):
                self.handle_offline_messages(message.get('messages', []))
            else:
                logging.error(f"获取离线消息失败: {message.get('error')}")
        elif msg_type == 'version_mismatch':
            server_version = message.get('server_version', '未知')
            client_version = message.get('client_version', '未知')
            messagebox.showerror("版本不匹配",
                                 f"客户端版本 ({client_version}) 与服务器版本 ({server_version}) 不匹配\n请更新客户端")
            self.connected = False
            self.socket.close()
            self.status_var.set("版本不匹配，连接已关闭")
        elif msg_type == 'version_ok':
            logging.info("版本检查通过")
        else:
            logging.warning(f"未知的消息类型: {msg_type}")

    def handle_incoming_call(self, message):
        call_type = message.get('call_type')
        caller_uuid = message.get('caller_uuid')

        # 查找呼叫者信息
        caller_name = "未知用户"
        for friend in self.friends:
            if friend['uuid'] == caller_uuid:
                caller_name = friend['username']
                break

        # 显示来电对话框
        result = messagebox.askyesno("来电", f"{caller_name} 请求 {call_type}通话，是否接听？")

        if result:
            # 接听通话
            self.accept_call(call_type, caller_name, False)
        else:
            # 拒绝通话
            pass

    def send_to_server(self, message):
        if not self.connected:
            messagebox.showerror("错误", "未连接到服务器")
            return False

        try:
            self.socket.send(json.dumps(message).encode('utf-8'))
            return True
        except Exception as e:
            logging.error(f"发送消息错误: {e}")
            self.status_var.set("发送消息失败")
            return False

    def show_login_dialog(self):
        if self.user:
            messagebox.showinfo("提示", "您已经登录了")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("登录")
        dialog.geometry("300x200")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text="用户名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.E)
        username_entry = ttk.Entry(dialog, width=20)
        username_entry.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        ttk.Label(dialog, text="密码:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.E)
        password_entry = ttk.Entry(dialog, width=20, show="*")
        password_entry.grid(row=1, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        auto_login_var = tk.BooleanVar(value=self.config.getboolean('AUTH', 'auto_login', fallback=False))
        ttk.Checkbutton(dialog, text="自动登录", variable=auto_login_var).grid(row=2, column=0, columnspan=2, pady=5)

        remember_pwd_var = tk.BooleanVar(value=auto_login_var.get())
        ttk.Checkbutton(dialog, text="记住密码", variable=remember_pwd_var).grid(row=3, column=0, columnspan=2, pady=5)

        def login():
            username = username_entry.get()
            password = password_entry.get()

            if not username or not password:
                messagebox.showerror("错误", "用户名和密码不能为空")
                return

            # 更新配置
            self.config['AUTH']['auto_login'] = str(auto_login_var.get())
            if remember_pwd_var.get() or auto_login_var.get():
                self.config['AUTH']['password'] = password
            else:
                self.config['AUTH']['password'] = ''
            self.save_config()

            self.send_to_server({
                'type': 'login',
                'username': username,
                'password': password
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=4, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="登录", command=login).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

        dialog.columnconfigure(1, weight=1)

    def show_register_dialog(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("注册")
        dialog.geometry("300x150")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text="用户名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.E)
        username_entry = ttk.Entry(dialog, width=20)
        username_entry.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        ttk.Label(dialog, text="密码:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.E)
        password_entry = ttk.Entry(dialog, width=20, show="*")
        password_entry.grid(row=1, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        def register():
            username = username_entry.get()
            password = password_entry.get()

            if not username or not password:
                messagebox.showerror("错误", "用户名和密码不能为空")
                return

            self.send_to_server({
                'type': 'register',
                'username': username,
                'password': password
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=2, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="注册", command=register).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

        dialog.columnconfigure(1, weight=1)

    def show_add_friend_dialog(self):
        if not self.user:
            messagebox.showerror("错误", "请先登录")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("添加好友")
        dialog.geometry("300x100")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text="用户名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.E)
        username_entry = ttk.Entry(dialog, width=20)
        username_entry.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        def add_friend():
            username = username_entry.get()

            if not username:
                messagebox.showerror("错误", "用户名不能为空")
                return

            self.send_to_server({
                'type': 'add_friend',
                'user_uuid': self.user['uuid'],
                'friend_username': username
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=1, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="添加", command=add_friend).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

        dialog.columnconfigure(1, weight=1)

    def show_block_user_dialog(self):
        if not self.user:
            messagebox.showerror("错误", "请先登录")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("拉黑用户")
        dialog.geometry("300x100")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text="用户名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.E)
        username_entry = ttk.Entry(dialog, width=20)
        username_entry.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        def block_user():
            username = username_entry.get()

            if not username:
                messagebox.showerror("错误", "用户名不能为空")
                return

            self.send_to_server({
                'type': 'block_user',
                'user_uuid': self.user['uuid'],
                'block_username': username
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=1, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="拉黑", command=block_user).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

        dialog.columnconfigure(1, weight=1)

    def show_create_group_dialog(self):
        if not self.user:
            messagebox.showerror("错误", "请先登录")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("创建群聊")
        dialog.geometry("300x100")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text="群名:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.E)
        group_name_entry = ttk.Entry(dialog, width=20)
        group_name_entry.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))

        def create_group():
            group_name = group_name_entry.get()

            if not group_name:
                messagebox.showerror("错误", "群名不能为空")
                return

            self.send_to_server({
                'type': 'create_group',
                'user_uuid': self.user['uuid'],
                'group_name': group_name
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=1, column=0, columnspan=2, pady=10)

        ttk.Button(button_frame, text="创建", command=create_group).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

        dialog.columnconfigure(1, weight=1)

    def show_emoji_dialog(self):
        if not self.user:
            messagebox.showerror("错误", "请先登录")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("选择表情")
        dialog.geometry("400x300")
        dialog.transient(self.root)
        dialog.grab_set()

        # 获取表情列表
        self.send_to_server({
            'type': 'get_emojis',
            'user_uuid': self.user['uuid']
        })

        # 等待表情列表更新
        self.root.after(500, lambda: self.populate_emoji_dialog(dialog))

    def populate_emoji_dialog(self, dialog):
        canvas = tk.Canvas(dialog)
        scrollbar = ttk.Scrollbar(dialog, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        # 暂时显示表情名称
        for i, emoji in enumerate(self.emojis):
            ttk.Button(scrollable_frame, text=emoji['name'],
                       command=lambda e=emoji: self.insert_emoji(e, dialog)).grid(
                row=i // 4, column=i % 4, padx=5, pady=5)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        ttk.Button(dialog, text="关闭", command=dialog.destroy).pack(pady=5)

    def insert_emoji(self, emoji, dialog):
        # 这里应该插入表情的表示，例如 :emoji_name:
        self.input_field.insert(tk.END, f":{emoji['name']}:")
        dialog.destroy()

    def send_file(self):
        if not self.user or not self.current_chat:
            messagebox.showerror("错误", "请先登录并选择聊天对象")
            return

        file_path = filedialog.askopenfilename(
            title="选择文件",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png *.gif"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        # 检查文件大小
        if os.path.getsize(file_path) > MAX_FILE_SIZE:
            messagebox.showerror("错误", f"文件大小不能超过 {MAX_FILE_SIZE // 1024}KB")
            return

        # 读取文件并编码
        with open(file_path, 'rb') as f:
            file_data = base64.b64encode(f.read()).decode('utf-8')

        # 发送文件
        self.send_to_server({
            'type': 'send_message',
            'sender_uuid': self.user['uuid'],
            'receiver_uuid': self.current_chat,
            'message_type': 'file',
            'content': file_data,
            'mode': 'normal',
            'file_name': os.path.basename(file_path)
        })

    def send_message(self):
        if not self.user or not self.current_chat:
            messagebox.showerror("错误", "请先登录并选择聊天对象")
            return

        message = self.input_field.get("1.0", tk.END).strip()
        if not message:
            return

        mode = self.mode_var.get()

        self.send_to_server({
            'type': 'send_message',
            'sender_uuid': self.user['uuid'],
            'receiver_uuid': self.current_chat,
            'content': message,
            'mode': mode,
            'message_type': 'text'
        })

        self.input_field.delete("1.0", tk.END)

    def display_message(self, sender_uuid, content, mode, msg_type, timestamp):
        # 获取发送者用户名
        sender_name = "未知用户"
        if sender_uuid == self.user['uuid']:
            sender_name = "我"
        else:
            for friend in self.friends:
                if friend['uuid'] == sender_uuid:
                    sender_name = friend['username']
                    break

        # 格式化时间
        try:
            time_obj = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
            time_str = time_obj.strftime("%H:%M:%S")
        except:
            time_str = "未知时间"

        # 启用聊天区域编辑
        self.chat_area.config(state=tk.NORMAL)

        # 插入发送者和时间
        self.chat_area.insert(tk.END, f"{sender_name} ({time_str}):\n", "sender")

        # 根据消息类型和模式处理内容
        if msg_type == 'text':
            if mode == 'normal':
                # 处理普通模式下的链接
                content = self.process_links(content)
                self.chat_area.insert(tk.END, content + "\n\n")
            elif mode == 'markdown':
                # 转换Markdown为HTML
                html_content = markdown.markdown(content)
                # 使用BeautifulSoup提取纯文本并保留基本格式
                soup = BeautifulSoup(html_content, 'html.parser')
                plain_text = soup.get_text()
                self.chat_area.insert(tk.END, plain_text + "\n")
                self.chat_area.insert(tk.END, "(Markdown模式)\n\n", "mode_tag")
            elif mode == 'html':
                # 使用BeautifulSoup清理HTML并提取文本
                soup = BeautifulSoup(content, 'html.parser')
                # 移除可能有害的标签和属性
                for tag in soup.find_all():
                    if tag.name not in ['b', 'i', 'u', 'em', 'strong', 'a', 'br', 'p']:
                        tag.unwrap()
                    for attr in list(tag.attrs.keys()):
                        if attr not in ['href']:
                            del tag.attrs[attr]

                plain_text = soup.get_text()
                self.chat_area.insert(tk.END, plain_text + "\n")
                self.chat_area.insert(tk.END, "(HTML模式)\n\n", "mode_tag")
        elif msg_type == 'file':
            # 处理文件消息
            try:
                file_data = base64.b64decode(content)
                # 保存文件到缓存目录
                file_name = f"file_{int(datetime.now().timestamp())}"
                file_path = os.path.join(IMAGE_CACHE_DIR, file_name)

                with open(file_path, 'wb') as f:
                    f.write(file_data)

                self.chat_area.insert(tk.END, f"[文件: {file_name}]\n\n")
            except Exception as e:
                logging.error(f"处理文件消息错误: {e}")
                self.chat_area.insert(tk.END, "[文件处理失败]\n\n")

        # 禁用聊天区域编辑并滚动到底部
        self.chat_area.config(state=tk.DISABLED)
        self.chat_area.see(tk.END)

    def process_links(self, text):
        # 使用正则表达式查找URL并替换为可点击格式
        url_pattern = r'(https?://[^\s]+)'
        return re.sub(url_pattern, r'\1', text)

    def on_friend_select(self, event):
        if not self.friends_listbox.curselection():
            return

        index = self.friends_listbox.curselection()[0]
        friend = self.friends[index]

        self.current_chat = friend['uuid']
        self.current_chat_type = 'user'
        self.chat_title.config(text=f"与 {friend['username']} 的聊天")

        # 清空聊天区域
        self.chat_area.config(state=tk.NORMAL)
        self.chat_area.delete(1.0, tk.END)
        self.chat_area.config(state=tk.DISABLED)

        # 加载本地历史消息
        self.load_local_messages(friend['uuid'])

    def show_friend_context_menu(self, event):
        # 获取点击的好友
        index = self.friends_listbox.nearest(event.y)
        if index < 0:
            return

        self.friends_listbox.selection_clear(0, tk.END)
        self.friends_listbox.selection_set(index)
        friend = self.friends[index]

        # 创建右键菜单
        menu = tk.Menu(self.root, tearoff=0)
        menu.add_command(label="发起语音通话", command=lambda: self.start_call_with_friend('voice', friend))
        menu.add_command(label="发起视频通话", command=lambda: self.start_call_with_friend('video', friend))
        menu.add_separator()
        menu.add_command(label="查看资料", command=lambda: self.show_user_info_dialog(friend))

        menu.post(event.x_root, event.y_root)

    def start_call_with_friend(self, call_type, friend):
        self.current_chat = friend['uuid']
        self.current_chat_type = 'user'
        self.start_call(call_type)

    def on_group_select(self, event):
        if not self.groups_listbox.curselection():
            return

        index = self.groups_listbox.curselection()[0]
        group = self.groups[index]

        self.current_chat = group['uuid']
        self.current_chat_type = 'group'
        self.chat_title.config(text=f"群聊: {group['name']}")

        # 清空聊天区域
        self.chat_area.config(state=tk.NORMAL)
        self.chat_area.delete(1.0, tk.END)
        self.chat_area.config(state=tk.DISABLED)

        # 加载本地历史消息
        self.load_local_messages(group['uuid'])

    def show_group_context_menu(self, event):
        # 获取点击的群组
        index = self.groups_listbox.nearest(event.y)
        if index < 0:
            return

        self.groups_listbox.selection_clear(0, tk.END)
        self.groups_listbox.selection_set(index)
        group = self.groups[index]

        # 创建右键菜单
        menu = tk.Menu(self.root, tearoff=0)

        # 根据用户在群组中的角色显示不同的菜单项
        if group['role'] in ['owner', 'admin']:
            menu.add_command(label="管理群组", command=lambda: self.show_group_management_dialog(group))

        menu.add_command(label="设置群昵称", command=lambda: self.set_group_nickname(group))
        menu.add_separator()
        menu.add_command(label="查看群信息", command=lambda: self.show_group_info(group))

        menu.post(event.x_root, event.y_root)

    def show_group_management_dialog(self, group):
        dialog = tk.Toplevel(self.root)
        dialog.title(f"管理群组: {group['name']}")
        dialog.geometry("400x300")
        dialog.transient(self.root)
        dialog.grab_set()

        # 获取群成员列表
        # 这里需要实现从服务器获取群成员列表的功能
        # 由于时间关系，暂不实现

        ttk.Label(dialog, text="群管理功能尚未完全实现").pack(pady=10)
        ttk.Button(dialog, text="关闭", command=dialog.destroy).pack(pady=5)

    def set_group_nickname(self, group):
        nickname = simpledialog.askstring("设置群昵称", "请输入群昵称:", initialvalue=group.get('custom_nickname', ''))
        if nickname is not None:
            self.send_to_server({
                'type': 'group_management',
                'action': 'set_nickname',
                'group_uuid': group['uuid'],
                'user_uuid': self.user['uuid'],
                'extra_data': nickname
            })

    def show_group_info(self, group):
        dialog = tk.Toplevel(self.root)
        dialog.title(f"群信息: {group['name']}")
        dialog.geometry("300x200")
        dialog.transient(self.root)
        dialog.grab_set()

        info_frame = ttk.Frame(dialog)
        info_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        ttk.Label(info_frame, text=f"群名称: {group['name']}").pack(anchor=tk.W, pady=5)
        ttk.Label(info_frame, text=f"群ID: {group['uuid']}").pack(anchor=tk.W, pady=5)
        ttk.Label(info_frame, text=f"你的角色: {group['role']}").pack(anchor=tk.W, pady=5)

        if group.get('title'):
            ttk.Label(info_frame, text=f"头衔: {group['title']}", foreground="purple").pack(anchor=tk.W, pady=5)

        ttk.Button(info_frame, text="关闭", command=dialog.destroy).pack(pady=10)

    def update_friends_list(self):
        self.friends_listbox.delete(0, tk.END)
        for friend in self.friends:
            self.friends_listbox.insert(tk.END, friend['username'])

    def update_groups_list(self):
        self.groups_listbox.delete(0, tk.END)
        for group in self.groups:
            display_name = group['name']
            if group.get('custom_nickname'):
                display_name = f"{display_name} ({group['custom_nickname']})"
            self.groups_listbox.insert(tk.END, display_name)

    def refresh_lists(self):
        if not self.user:
            return

        self.send_to_server({
            'type': 'get_friends',
            'user_uuid': self.user['uuid']
        })

        self.send_to_server({
            'type': 'get_groups',
            'user_uuid': self.user['uuid']
        })

    def search_users_groups(self):
        query = self.search_var.get().strip()
        if not query:
            return

        self.send_to_server({
            'type': 'search_users_groups',
            'query': query
        })

    def show_search_results(self, users, groups):
        dialog = tk.Toplevel(self.root)
        dialog.title("搜索结果")
        dialog.geometry("400x300")
        dialog.transient(self.root)
        dialog.grab_set()

        notebook = ttk.Notebook(dialog)
        notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 用户结果页
        user_frame = ttk.Frame(notebook)
        notebook.add(user_frame, text=f"用户 ({len(users)})")

        user_listbox = tk.Listbox(user_frame)
        user_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        for user in users:
            user_listbox.insert(tk.END, user['username'])

        user_listbox.bind('<<ListboxSelect>>', lambda e: self.on_search_user_select(users, user_listbox))

        # 群组结果页
        group_frame = ttk.Frame(notebook)
        notebook.add(group_frame, text=f"群组 ({len(groups)})")

        group_listbox = tk.Listbox(group_frame)
        group_listbox.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        for group in groups:
            group_listbox.insert(tk.END, group['name'])

        group_listbox.bind('<<ListboxSelect>>', lambda e: self.on_search_group_select(groups, group_listbox))

        ttk.Button(dialog, text="关闭", command=dialog.destroy).pack(pady=10)

    def on_search_user_select(self, users, listbox):
        selection = listbox.curselection()
        if not selection:
            return

        index = selection[0]
        user = users[index]

        self.send_to_server({
            'type': 'get_user_info',
            'username': user['username']
        })

    def on_search_group_select(self, groups, listbox):
        selection = listbox.curselection()
        if not selection:
            return

        index = selection[0]
        group = groups[index]

        # 显示群组信息
        self.show_group_info({'uuid': group['uuid'], 'name': group['name'], 'role': 'unknown'})

    def show_user_info_dialog(self, user_info):
        dialog = tk.Toplevel(self.root)
        dialog.title(f"用户信息: {user_info['username']}")
        dialog.geometry("300x200")
        dialog.transient(self.root)
        dialog.grab_set()

        info_frame = ttk.Frame(dialog)
        info_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        ttk.Label(info_frame, text=f"用户名: {user_info['username']}").pack(anchor=tk.W, pady=5)
        ttk.Label(info_frame, text=f"用户ID: {user_info['uuid']}").pack(anchor=tk.W, pady=5)

        # 显示头像
        if user_info.get('avatar'):
            try:
                avatar_data = base64.b64decode(user_info['avatar'])
                image = Image.open(io.BytesIO(avatar_data))
                image = image.resize((50, 50), Image.Resampling.LANCZOS)
                photo = ImageTk.PhotoImage(image)

                avatar_label = ttk.Label(info_frame, image=photo)
                avatar_label.image = photo  # 保持引用
                avatar_label.pack(pady=5)
            except Exception as e:
                logging.error(f"显示头像错误: {e}")

        button_frame = ttk.Frame(info_frame)
        button_frame.pack(pady=10)

        ttk.Button(button_frame, text="添加好友",
                   command=lambda: self.add_friend_by_username(user_info['username'])).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="关闭", command=dialog.destroy).pack(side=tk.LEFT, padx=5)

    def add_friend_by_username(self, username):
        self.send_to_server({
            'type': 'add_friend',
            'user_uuid': self.user['uuid'],
            'friend_username': username
        })

    def download_chat_history(self):
        if not self.user or not self.current_chat:
            messagebox.showerror("错误", "请先登录并选择聊天对象")
            return

        self.send_to_server({
            'type': 'download_chat_history',
            'user_uuid': self.user['uuid'],
            'target_uuid': self.current_chat,
            'chat_type': self.current_chat_type
        })

    def save_chat_history(self, messages):
        if not messages:
            messagebox.showinfo("提示", "没有聊天记录可下载")
            return

        file_path = filedialog.asksaveasfilename(
            title="保存聊天记录",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                for msg in messages:
                    timestamp = msg['timestamp']
                    sender = msg['sender_uuid']
                    content = msg['content']

                    # 查找发送者用户名
                    sender_name = "未知用户"
                    if sender == self.user['uuid']:
                        sender_name = self.user['username']
                    else:
                        for friend in self.friends:
                            if friend['uuid'] == sender:
                                sender_name = friend['username']
                                break

                    f.write(f"[{timestamp}] {sender_name}: {content}\n")

            messagebox.showinfo("成功", "聊天记录下载完成")
        except Exception as e:
            logging.error(f"保存聊天记录错误: {e}")
            messagebox.showerror("错误", "保存聊天记录失败")

    def start_call(self, call_type):
        if not self.user or not self.current_chat:
            messagebox.showerror("错误", "请先登录并选择聊天对象")
            return

        if self.current_chat_type != 'user':
            messagebox.showerror("错误", "只能与好友进行通话")
            return

        # 查找目标用户信息
        target_name = "未知用户"
        for friend in self.friends:
            if friend['uuid'] == self.current_chat:
                target_name = friend['username']
                break

        # 发送呼叫请求
        self.send_to_server({
            'type': 'start_call',
            'call_type': call_type,
            'target_uuid': self.current_chat,
            'caller_uuid': self.user['uuid']
        })

        # 启动本地通话界面
        self.accept_call(call_type, target_name, True)

    def accept_call(self, call_type, target_name, is_caller):
        # 关闭现有通话（如果有）
        if self.active_call:
            self.active_call.end_call()

        # 启动新通话
        self.active_call = VideoCall(self.root, call_type, is_caller, target_name)

    def show_user_profile_dialog(self):
        if not self.user:
            messagebox.showerror("错误", "请先登录")
            return

        dialog = tk.Toplevel(self.root)
        dialog.title("个人资料")
        dialog.geometry("400x500")
        dialog.transient(self.root)
        dialog.grab_set()

        # 创建头像预览
        avatar_frame = ttk.Frame(dialog)
        avatar_frame.pack(pady=10)

        self.avatar_image = None
        self.avatar_label = ttk.Label(avatar_frame)
        self.avatar_label.pack()

        # 加载当前头像
        if self.user.get('avatar'):
            try:
                avatar_data = base64.b64decode(self.user['avatar'])
                image = Image.open(io.BytesIO(avatar_data))
                image = image.resize((100, 100), Image.Resampling.LANCZOS)
                self.avatar_image = ImageTk.PhotoImage(image)
                self.avatar_label.config(image=self.avatar_image)
            except Exception as e:
                logging.error(f"加载头像错误: {e}")

        ttk.Button(avatar_frame, text="更换头像", command=self.change_avatar).pack(pady=5)

        # 用户名修改
        ttk.Label(dialog, text="用户名:").pack(anchor=tk.W, padx=20, pady=(10, 5))
        username_var = tk.StringVar(value=self.user['username'])
        username_entry = ttk.Entry(dialog, textvariable=username_var, width=30)
        username_entry.pack(anchor=tk.W, padx=20, pady=(0, 10))

        # 自动登录设置
        auto_login_var = tk.BooleanVar(value=self.config.getboolean('AUTH', 'auto_login', fallback=False))
        ttk.Checkbutton(dialog, text="自动登录", variable=auto_login_var).pack(anchor=tk.W, padx=20, pady=5)

        def save_profile():
            new_username = username_var.get().strip()
            if not new_username:
                messagebox.showerror("错误", "用户名不能为空")
                return

            # 更新配置
            self.config['AUTH']['auto_login'] = str(auto_login_var.get())
            self.save_config()

            # 发送更新请求
            self.send_to_server({
                'type': 'update_profile',
                'user_uuid': self.user['uuid'],
                'new_username': new_username,
                'avatar_data': self.new_avatar_data if hasattr(self, 'new_avatar_data') else None
            })

            dialog.destroy()

        button_frame = ttk.Frame(dialog)
        button_frame.pack(pady=20)

        ttk.Button(button_frame, text="保存", command=save_profile).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)

    def change_avatar(self):
        file_path = filedialog.askopenfilename(
            title="选择头像",
            filetypes=[("图片文件", "*.jpg *.jpeg *.png *.gif")]
        )

        if not file_path:
            return

        try:
            # 打开并调整图片大小
            image = Image.open(file_path)
            image = image.resize((100, 100), Image.Resampling.LANCZOS)

            # 转换为圆形头像
            mask = Image.new('L', (100, 100), 0)
            draw = ImageDraw.Draw(mask)
            draw.ellipse((0, 0, 100, 100), fill=255)

            output = Image.new('RGBA', (100, 100))
            output.paste(image, (0, 0), mask=mask)

            # 显示预览
            self.avatar_image = ImageTk.PhotoImage(output)
            self.avatar_label.config(image=self.avatar_image)

            # 保存为Base64编码
            buffered = io.BytesIO()
            output.save(buffered, format="PNG")
            self.new_avatar_data = base64.b64encode(buffered.getvalue()).decode('utf-8')

        except Exception as e:
            logging.error(f"更换头像错误: {e}")
            messagebox.showerror("错误", "更换头像失败")

    def save_message_to_local(self, message):
        """保存消息到本地文件"""
        try:
            # 确定聊天对象的UUID
            chat_uuid = self.get_chat_uuid(message)

            # 创建消息存储目录
            chat_dir = os.path.join(MESSAGE_STORAGE_DIR, chat_uuid)
            os.makedirs(chat_dir, exist_ok=True)

            # 消息文件路径
            msg_file = os.path.join(chat_dir, f"{chat_uuid}.msg")

            # 将消息追加到文件
            with open(msg_file, 'a', encoding='utf-8') as f:
                f.write(json.dumps(message) + '\n')

        except Exception as e:
            logging.error(f"保存消息到本地错误: {e}")

    def load_local_messages(self, chat_uuid):
        """从本地文件加载消息"""
        try:
            # 消息文件路径
            msg_file = os.path.join(MESSAGE_STORAGE_DIR, chat_uuid, f"{chat_uuid}.msg")

            if not os.path.exists(msg_file):
                return

            # 读取消息
            with open(msg_file, 'r', encoding='utf-8') as f:
                for line in f:
                    message = json.loads(line.strip())
                    self.display_message(
                        message.get('sender_uuid'),
                        message.get('content'),
                        message.get('mode', 'normal'),
                        message.get('message_type', 'text'),
                        message.get('timestamp')
                    )

        except Exception as e:
            logging.error(f"加载本地消息错误: {e}")

    def get_chat_uuid(self, message):
        """根据消息确定聊天对象的UUID"""
        sender_uuid = message.get('sender_uuid')
        receiver_uuid = message.get('receiver_uuid')

        # 如果是群消息
        if receiver_uuid.startswith('Z'):
            return receiver_uuid

        # 如果是私聊消息
        if sender_uuid == self.user['uuid']:
            return receiver_uuid
        else:
            return sender_uuid

    def get_offline_messages(self):
        """获取离线消息"""
        if not self.user:
            return

        self.send_to_server({
            'type': 'get_offline_messages',
            'user_uuid': self.user['uuid']
        })

    def handle_offline_messages(self, messages):
        """处理离线消息"""
        for message in messages:
            self.display_message(
                message.get('sender_uuid'),
                message.get('content'),
                message.get('mode', 'normal'),
                message.get('message_type', 'text'),
                message.get('timestamp')
            )
            # 保存消息到本地
            self.save_message_to_local(message)

        # 删除服务器上的离线消息
        self.send_to_server({
            'type': 'delete_offline_messages',
            'user_uuid': self.user['uuid']
        })


def main():
    # 检查是启动服务器还是客户端
    import sys
    if len(sys.argv) > 1 and sys.argv[1] == '--server':
        server = ChatServer()
        server.start()
    else:
        root = tk.Tk()
        app = ChatClient(root)
        root.mainloop()


if __name__ == "__main__":
    main()